package com.lu.netty.io.server

import android.text.TextUtils
import androidx.annotation.IntRange
import com.lu.netty.io.core.EventType
import com.lu.netty.io.core.IoConnector
import com.lu.netty.io.core.IoState
import com.lu.netty.io.core.NettyConfigure
import com.lu.netty.io.core.NettyConnector
import com.lu.netty.io.core.NettyLog
import io.netty.bootstrap.ServerBootstrap
import io.netty.channel.Channel
import io.netty.channel.ChannelFuture
import io.netty.channel.ChannelFutureListener
import io.netty.channel.ChannelOption
import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.socket.nio.NioServerSocketChannel
import java.io.IOException

/**
 * File Name :NettyServer
 * Author : luqihua
 * Date : 2024/4/15
 * Desc :
 **/

abstract class ANettyServer(
    @IntRange(from = 1000, to = 65535)
    private val port: Int,
) : IoConnector(), Runnable, IServerAdapterCallback {

    companion object {
        private const val TAG = "NettyServer"
    }

    private var serverChannel: Channel? = null

    private val connectorList = ArrayList<NettyConnector>()

    //事件循环组
    private var bootstrap = ServerBootstrap()

    //线程池
    private var loopGroup = NioEventLoopGroup()


    fun getClientList(): List<NettyConnector> {
        return connectorList
    }


    private fun reset() {
        setIoState(IoState.INIT)
        bootstrap = ServerBootstrap()
        bootstrap.group(loopGroup, loopGroup)
            .channel(NioServerSocketChannel::class.java)
            .childHandler(ServerChannelHandler(this))
            .option(ChannelOption.SO_KEEPALIVE, true)
            .option(ChannelOption.TCP_NODELAY, true)
            .option(ChannelOption.SO_BACKLOG, 128)
    }

    override fun start() {
        NettyConfigure.executor.execute(this)
    }

    override fun release() {
        if (getIoState() != IoState.RELEASE) {
            connectorList.onEach {
                it.close()
            }.clear()
            try {
                serverChannel?.close()
                loopGroup.shutdownGracefully()
            } catch (e: Exception) {
                NettyLog.e(TAG, "release:", e)
            }
        }
    }

    override fun close() {
        if (getIoState() < IoState.CLOSE) {
            connectorList.onEach {
                it.close()
            }.clear()
            serverChannel?.close()
        }
    }


    override fun run() {
        if (!checkCanConnect()) {
            return
        }
        reset()
        setIoState(IoState.CONNECTING)

        try {

            val channelFuture = bootstrap.bind(port).sync()

            channelFuture.addListener(object : ChannelFutureListener {
                override fun operationComplete(future: ChannelFuture) {
                    if (future.isSuccess) {
                        NettyLog.d(TAG, "future bind success $port")
                    } else {
                        setIoState(IoState.INIT)
                        NettyLog.d(TAG, "future bind failed")
                    }
                }
            })
            serverChannel = channelFuture.channel()
            channelFuture.channel().closeFuture().sync()
            setIoState(IoState.CLOSE)
            NettyLog.d(TAG, "closeFuture 关闭成功")
        } catch (e: IOException) {
            setIoState(IoState.INIT)
            e.printStackTrace()
        } finally {
            try {
                loopGroup.shutdownGracefully().sync()
                close()
                NettyLog.d(TAG, "finally")
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    }

    fun send(key: String, message: String) {
        if (TextUtils.isEmpty(key)) {
            connectorList.forEach {
                it.send(message)
            }
        } else {
            connectorList.find { it.key == key }?.send(message)
        }
    }

    open fun sendAll(message: String) {
        connectorList.forEach {
            it.send(message)
        }
    }

    override fun onEvent(channel: Channel, type: EventType) {
        NettyLog.d(TAG, "onEvent: channel = ${channel.hashCode()}  type=$type")
        when (type) {
            is EventType.DISCONNECTED -> {
                removeClient(channel)
            }

            is EventType.CONNECTED -> {
                acceptNewClient(channel)
            }

            is EventType.Error -> {
                removeClient(channel)
                setIoState(IoState.ERROR)
            }
        }
    }

    /**
     * 接收新的客户端连接
     */
    protected open fun acceptNewClient(channel: Channel) {
        val connector = NettyConnector(channel)
        connectorList.add(connector)
    }

    /**
     * 客户端连接断开
     */
    protected open fun removeClient(channel: Channel) {
        connectorList.find { it.matchChannel(channel) }?.let { find ->
            find.close()
            connectorList.remove(find)
        }
    }

    override fun onMessageArrived(channel: Channel, message: String) {
        onMessageArrived(connector = NettyConnector(channel), message)
    }

    abstract fun onClientChange(connector: NettyConnector, isAccept: Boolean)
    abstract fun onMessageArrived(connector: NettyConnector, message: String)
}